Lambda AuthorizerがDenyを返すとき、APIのステータスコードを403ではなく、401で返す with OpenAPI
ログインして利用するWebアプリとAPIを開発しているとき、アクセストークンの認証失敗時、または、Lambda AuthorizerでDeny
と判断することがあります。
このとき、デフォルトでは、ステータスコード403が返ります。
今回は、Lambda AuthorizerでDeny
と判定したとき、APIのステータスコードを401にしてみます。
おすすめの方
- Lambda Authorizerで認証失敗時、403以外のステータスコードを返したい方
- AWS SAMとOpenAPIでAPI GatewayとLambdaをデプロイしたい方
Lambda Authorizerをデプロイする
sam init
sam init \ --runtime python3.9 \ --name Lambda-Authorizer-Status-Code-Sample \ --app-template hello-world \ --no-tracing \ --package-type Zip
SAMテンプレート
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Lambda-Authorizer-Status-Code-Sample Resources: MyApi: Type: AWS::Serverless::Api Properties: StageName: dev DefinitionBody: Fn::Transform: Name: AWS::Include Parameters: Location: s3://cm-fujii.genki-deploy/Lambda-Authorizer-Status-Code-Sample-Stack/api.yaml # Lambda Authorizer AuthorizerFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: authorizer.lambda_handler Runtime: python3.9 Timeout: 5 AuthorizerFunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${AuthorizerFunction} AuthorizerFunctionPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt AuthorizerFunction.Arn Principal: apigateway.amazonaws.com SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${MyApi}/authorizers/* HelloWorldFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.9 Timeout: 5 Events: HelloWorld: Type: Api Properties: Path: /hello Method: get RestApiId: !Ref MyApi HelloWorldFunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${HelloWorldFunction} Outputs: HelloWorldApi: Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/dev/hello"
OpenAPI
openapi: 3.0.1 info: title: sample api version: 1.0.0 # https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/supported-gateway-response-types.html x-amazon-apigateway-gateway-responses: ACCESS_DENIED: statusCode: 401 paths: /hello: get: tags: - hello responses: 200: $ref: "#/components/responses/200" 401: $ref: "#/components/responses/401" 403: $ref: "#/components/responses/403" 500: $ref: "#/components/responses/500" security: - MyLambdaAuthorizer: [] x-amazon-apigateway-integration: type: aws_proxy uri: 'Fn::Sub': >- arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloWorldFunction.Arn}/invocations httpMethod: POST responses: default: statusCode: 200 passthroughBehavior: when_no_templates contentHandling: CONVERT_TO_TEXT components: securitySchemes: MyLambdaAuthorizer: type: apiKey name: Authorization in: header x-amazon-apigateway-authtype: custom x-amazon-apigateway-authorizer: authorizerUri: 'Fn::Sub': >- arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AuthorizerFunction.Arn}/invocations authorizerResultTtlInSeconds: 0 type: token responses: 200: description: Success 401: description: Unauthorized 403: description: User or client is not authorized. 500: description: Internal Server Error
Lambda Authorizer
今回は、常にDenyを返します。
def lambda_handler(event, context): return { 'principalId': '*', 'policyDocument': { 'Version': '2012-10-17', 'Statement': [ { 'Action': 'execute-api:Invoke', 'Effect': 'Deny', 'Resource': event['methodArn'] } ] } }
Lambdaコード
import json def lambda_handler(event, context): return { "statusCode": 200, "body": json.dumps({ "message": "hello world", }), }
デプロイ
aws s3 cp \ api.yaml \ s3://cm-fujii.genki-deploy/Lambda-Authorizer-Status-Code-Sample-Stack/api.yaml sam package \ --output-template-file packaged.yaml \ --s3-bucket cm-fujii.genki-deploy sam deploy \ --template-file packaged.yaml \ --stack-name Lambda-Authorizer-Status-Code-Sample-Stack \ --s3-bucket cm-fujii.genki-deploy \ --capabilities CAPABILITY_NAMED_IAM \ --no-fail-on-empty-changeset
動作確認
APIエンドポイントを取得する
aws cloudformation describe-stacks \ --stack-name Lambda-Authorizer-Status-Code-Sample-Stack \ --query 'Stacks[].Outputs'
Authorizationなし
401が返ってきました。
$ curl -D - https://aaa.execute-api.ap-northeast-1.amazonaws.com/dev/hello HTTP/2 401 ... {"message":"Unauthorized"}
Authorizationあり
何もしなければ403が返ってきますが、401が返ってきました。期待通りです。
curl -D - https://aaa.execute-api.ap-northeast-1.amazonaws.com/dev/hello \ --header 'authorization: xxx' HTTP/2 401 ... {"message":"User is not authorized to access this resource with an explicit deny"}
さいごに
API Gatewayのゲートウェイレスポンスを利用して、ステータスコードを401に変更してみました。 API Gatewayのゲートウェイレスポンスは、ほかにもいろいろな種類や変更できる箇所があるので、必要に応じて活用したいです。